<?php
/**
 * crcl/base
 *
 * Copyright 2013 CRCL - André Sabosch - info@crcl.de
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Tier
 *
 * Detects where crcl\base is running
 */

namespace crcl\base;

interface ITier
{
  /**
   * Is base running on a local installation? e.g. developer machine
   * - uses environment variable "BASE_TIER". if value = LOCAL, isLocal returns TRUE
   * - if environment variable is not set, isLocal tries to read HTTP_HOST (.intra, .local)
   *
   * @static
   * @return bool
   */
  public static function isLocal();

  /**
   * Is crclBase running on integration server?
   * - uses environment variable "BASE_TIER". if value = INTEGRATION, isIntegration returns TRUE
   * - if environment variable is not set, isLocal tries to read HTTP_HOST (integration.crcl.de)
   *
   * @static
   * @return bool
   */
  public static function isIntegration();

  /**
   * Is crclBase running on staging server?
   * - uses environment variable "BASE_TIER". if value = STAGING, isIntegration returns TRUE
   * - if environment variable is not set, isLocal tries to read HTTP_HOST (staging.crcl.de)
   *
   * @static
   * @return bool
   */
  public static function isStaging();

  /**
   * Is base running on a production server?
   * isProduction is TRUE if base is not running on a local, integration or staging machine
   *
   * @static
   * @return bool
   */
  public static function isProduction();

  /**
   * Replaces %TIER% in string with current tier
   *
   * @static
   * @param string $sInput
   * @param boolean $bLower returns tier in lowercase
   *
   * @return  string
   */
  public static function replaceConstants($sInput, $bLower = \FALSE);

  /**
   * Returns current tier
   *
   * @static
   * @return  string  @see constants LOCAL, INTEGRATION, STAGING, PRODUCTION
   */
  public static function get();
}

abstract class Tier implements ITier
{
  const LOCAL       = 'LOCAL';
  const INTEGRATION = 'INTEGRATION';
  const STAGING     = 'STAGING';
  const PRODUCTION  = 'PRODUCTION';

  const ENV_TIER = 'BASE_TIER';

  public static function isLocal()
  {
    return self::_evaluateTier(self::LOCAL, array('.intra', '.local', 'local.'));
  }

  public static function isIntegration()
  {
    return self::_evaluateTier(self::INTEGRATION, array('integration.'));
  }

  public static function isStaging()
  {
    return self::_evaluateTier(self::STAGING, array('staging.'));
  }

  public static function isProduction()
  {
    //check overrides first
    if (\strtolower(\getenv(self::ENV_TIER)) == \strtolower(self::PRODUCTION))
    {
      return \TRUE;
    }

    if ((!self::isLocal()) && (!self::isIntegration()) && (!self::isStaging()))
    {
      return \TRUE;
    }
    else
    {
      return \FALSE;
    }
  }

  public static function replaceConstants($sInput, $bLower = \FALSE)
  {
    $sTier = self::_getTier();
    if ($bLower)
    {
      $sTier = \strtolower($sTier);
    }
    $sReturn = \str_replace('%TIER%', $sTier, $sInput);
    return $sReturn;
  }

  public static function get()
  {
    return self::_getTier();
  }

  public static function toString()
  {
    return self::_getTier();
  }

  /** PRIVATE *************************************************************************************/

  private static function _evaluateTier($sTier, $aHost)
  {
    //check environment - allows overrides
    if (\strtolower(\getenv(self::ENV_TIER)) == \strtolower($sTier))
    {
      return \TRUE;
    }
    //check uname('n') (hostname)
    $sHostname = \php_uname('n');
    foreach ($aHost as $sHost)
    {
      if ($sHostname == $sHost)
      {
        //how should we force this on local tests?
        //@codeCoverageIgnoreStart
        return \TRUE;
        //@codeCoverageIgnoreEnd
      }
    }

    //try HTTP_HOST
    //remember: when running on CLI, we got no HTTP_HOST
    if (isset($_SERVER['HTTP_HOST']))
    {
      foreach ($aHost as $sHost)
      {
        if (\strpos($_SERVER['HTTP_HOST'], $sHost) !== \FALSE)
        {
          return \TRUE;
        }
      }
    }
    //defaults to FALSE
    return \FALSE;
  }

  private static function _getTier()
  {
    //check environment - we have to do this first!
    if (\strtolower(\getenv(self::ENV_TIER)) == \strtolower(self::PRODUCTION))
    {
      return self::PRODUCTION;
    }
    if (\strtolower(\getenv(self::ENV_TIER)) == \strtolower(self::STAGING))
    {
      return self::STAGING;
    }
    if (\strtolower(\getenv(self::ENV_TIER)) == \strtolower(self::INTEGRATION))
    {
      return self::INTEGRATION;
    }
    if (\strtolower(\getenv(self::ENV_TIER)) == \strtolower(self::LOCAL))
    {
      return self::LOCAL;
    }

    if (self::isProduction())
    {
      return self::PRODUCTION;
    }
    elseif (self::isStaging())
    {
      return self::STAGING;
    }
    elseif (self::isIntegration())
    {
      return self::INTEGRATION;
    }
    elseif (self::isLocal())
    {
      return self::LOCAL;
    }
    //should not occur
    //@codeCoverageIgnoreStart
    return self::PRODUCTION;
    //@codeCoverageIgnoreEnd
  }
}